﻿#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


//结构体实现位段
//位段  1. 成员必须是整形，c99中也可以是其他类型
//      2. 位段的成员名后面有冒号和数字
//      3. 针对跨平台代码时，尽量不要写位段
//    位  表示的是2进制位
//  位段的内存分配
//  位段的空间上是按需要以4个字节（int）或一个字节(char)的方式开辟
//struct A
//{
//	int _a : 2;//只占2个比特位
//	int _b : 5;
//	int _c : 10;
//	int _d : 30;
//};
//int main()
//{
//	printf("%zd\n", sizeof(struct A));
//
//	return 0;
//}

// 1. char - 一个字节一个字节开辟
// 2. 字节内部从右向左使用
// 3. 剩余的bit为不够下一个成员使用时，浪费掉重新开辟新的空间

//跟结构体相比，位段可以达到同样的效果，并且可以很好的节省空间，但是有跨平台问题存在
//不能对位段的成员取地址
//struct S
//{
//	char a : 3;
//	char b : 4;
//	char c : 5;
//	char d : 4;
//};
//int main()
//{
//	struct S s = { 0 };
//	printf("%zd\n", sizeof(struct S));
//	//scanf("%d", &s.b);
//	//可以赋值，但是不能直接输入
//	s.a = 10;
//	s.b = 12;
//	s.c = 3;
//	s.d = 4;
//
//
//	return 0;
//}



//自定义类型：联合和枚举

//联合体
//编译器只为最大的成员分配足够的内存空间，联合体的特点是所以成员共用同一块空间
//联合体也称：共用体
//struct S
//{
//	char c;
//	int i;
//};
//
//union U
//{
//	char c;
//	int i;
//};
//int main()
//{
//	printf("%zd\n", sizeof(struct S));
//	printf("%zd\n", sizeof(union U));
//
//	return 0;
//}

//联合体的特点
//union U
//{
//	char c;
//	int i;
//};
//int main()
//{
//	printf("%zd\n", sizeof(union U));//4
//	union U u;
//	//地址相同
//	printf("%p\n", &u);
//	printf("%p\n", &(u.c));
//	printf("%p\n", &(u.i));
//
//	return 0;
//}


//union U
//{
//	char c;
//	int i;
//};
//
//int main()
//{
//	union U u;
//	u.i = 0x11223344;
//	u.c = 0;
//
//	return 0;
//}




//联合体的大小至少是最大成员的大小
//
//union un
//{
//	char c[5];//5  1    8    1
//	int i;//4      4    8    4
//};
//
//int main()
//{
//	printf("%zd\n", sizeof(union un));//8
//
//	return 0;
//}

//
//union un2
//{
//	short s[7];//14      2   8   2
//	int i;//      4      4   8   4
//};
//
//int main()
//{
//	printf("%zd\n", sizeof(union un2));
//
//	return 0;
//}


//联合体的使用
//
//struct gift_list
//{
//	int stoct_number;//库存量
//	double price;//价格
//	int item_type;//商品类型
//
//	union {
//		struct
//		{
//			char title[10];//书名
//			char author[20];//作者
//			int num_pages;//页数
//		}book;
//		struct {
//			char design[30];//设计
//		}mug;
//		struct
//		{
//			char design[20];//设计
//			int color;//颜色
//			int sizer;//尺寸
//		}shirt;
//	}item;
//};


//写代码判断大小端

//int main()
//{
//	int  a = 1;
//	//0x00 00 00 01
//	//        大端
//	//    低                              高
//	//        00  00  00  01
//	//       小端
//	//    低                              高
//	//        01  00  00  00
//	if (*(char*)&a == 1)
//	{
//		printf("小端\n");
//	}
//	else {
//		printf("大端\n");
//	}
//
//	return 0;
//}

//int check_sys()
//{
//	union
//	{
//		int i;
//		char c;
//	}un;
//	un.i = 1;
//	return un.c;
//}
//int main()
//{
//	int ret = check_sys();
//	if (ret == 1)
//	{
//		printf("小端\n");
//	}
//	else {
//		printf("大端\n");
//	}
//
//	return 0;
//}




//枚举
//枚举的优点：
//   1. 增加代码的可读性和可维护性
//   2. 和#define定义的标识符⽐较枚举有类型检查，更加严谨。
//   3. 便于调试，预处理阶段会删除 #define 定义的符号
//   4. 使⽤⽅便，⼀次可以定义多个常量
//   5. 枚举常量是遵循作⽤域规则的，枚举声明在函数内，只能在函数内使⽤
//enum Sex
//{
//	//枚举类型的可能取值
//	MALE,
//	FEMALE,
//	SECRET
//};
//
//enum Color
//{
//	//常量--枚举常量
//	//是可以赋初值的
//	RED,
//	GREEN,
//	BLUE
//};
//int main()
//{
//	printf("%d\n", MALE);
//	printf("%d\n", FEMALE);
//	printf("%d\n", SECRET);//默认是0,1,2
//
//	return 0;
//}





//动态内存管理
// 动态内存分配
//   1 主动申请
//   2 大小调整
//   3 释放
//struct S
//{
//	char name[20];
//	int age;
//};
//
//int main()
//{
//	int a = 10;//一次性开辟一块空间 - 4个字节
//	int arr[5];//一块连续的空间 - 20个字节
//
//	return 0;
//}

//malloc  向内存申请一块连续的空间
//int main()
//{
//	int* p = (int*)malloc(10 * sizeof(int));
//	//int* p = (int*)malloc(INT_MAX);//分配空间错误
//	if (p == NULL)
//	{
//		perror("malloc");
//		return 1;//异常返回
//	}
//	//使用空间
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		*(p + i) = i + 1;
//	}
//
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//
//	free(p);//要释放的动态内存的起始地址
//	p = NULL;//最后将p置为NULL，防止出现野指针
//
//	//如果不free
//	//  程序不结束，不使用的时候也会一直占用
//
//	//如果程序结束，操作系统也是会回收空间
//
//	//如果free释放的空间不是动态分配的话，程序就会崩掉
//	//free的是NULL什么都不做
//
//
//	return 0;
//}


//colloc和realloc
//C语⾔还提供了⼀个函数叫 calloc ， calloc 函数也⽤来动态内存分配
//void* calloc (size_t num, size_t size)

//函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间，并且把空间的每个字节初始化为0
//与malloc的区别是会全部初始化为0
//int main()
//{
//	//int* p = (int*)malloc(10 * sizeof(int));
//	int* p = (int*)calloc(10, sizeof(int));
//	if (p == NULL)
//	{
//		perror("calloc");
//		return 1;
//	}
//	for (int i = 0; i < 10; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//
//
//
//	//释放
//	free(p);
//	p = NULL;
//	return 0;
//}



//realloc函数的出现让动态内存管理更加灵活
// void* realloc (void* ptr, size_t size)
//            ptr 要调整内存的地址   size是调整之后的大小
//     返回值是调整之后的内存起始位置
//这个函数调整原内存空间⼤⼩的基础上，还会将原来内存中的数据移动到新的空间
//realloc在调整内存空间的是存在两种情况：
//◦ 情况1：原有空间之后有⾜够⼤的空间
//◦ 情况2：原有空间之后没有⾜够⼤的空间,拷贝原有数据之后，会释放原来空间
//◦ 情况3：失败了。

//int main()
//{
//	int* p = (int*)malloc(20);
//	if (p == NULL)
//	{
//		perror("malloc");
//	}
//	//使用
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		*(p + i) = i + 1;
//	}
//
//	//扩大空间
//	int* ptr = (int*)realloc(p, 40);
//	if (ptr != NULL)
//	{
//		p = ptr;
//	}
//	else {
//		perror("realloc");
//		return 1;
//	}
//	for (i = 5; i < 10; i++)
//	{
//		*(p + i) = i + 1;
//	}
//
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//
//	free(p);
//	p = NULL;
//	return 0;
//}


//realloc也可以实现malloc的功能
int main()
{

	int* p = (int*)realloc(NULL, 20);//等价于malloc(20)

	free(p);
	p = NULL;

	return 0;
}